﻿using System;

using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

using xEngine.Maths;

namespace xEngine.Rendering
{
    public class Shapes
    {
        public static void DrawCube(GraphicsDevice device, Vector3 color, Vector3 size) { DrawCube(device, color, Vector3.Zero, size); }
        public static void DrawCube(GraphicsDevice device, Vector3 color, Vector3 start, Vector3 size)
        {
            VertexPositionColor[] v = new VertexPositionColor[8];
            int[] i = new int[24];
            VertexBuffer vb = new VertexBuffer(device, VertexPositionColor.VertexDeclaration, 8, BufferUsage.WriteOnly);
            IndexBuffer ib = new IndexBuffer(device, typeof(int), 24, BufferUsage.WriteOnly);

            for (int y = 0; y < 2; y++)
                for (int z = 0; z < 2; z++)
                    for (int x = 0; x < 2; x++)
                    {
                        v[x + z * 2 + y * 4].Position = new Vector3(start.X + size.X * x, start.Y + size.Y * y, start.Z + size.Z * z);
                        v[x + z * 2 + y * 4].Color = new Color(color);
                    }

            int index = 0;

            i[index++] = 0; i[index++] = 1;
            i[index++] = 2; i[index++] = 3;
            i[index++] = 0; i[index++] = 2;
            i[index++] = 1; i[index++] = 3;

            i[index++] = 4; i[index++] = 5;
            i[index++] = 6; i[index++] = 7;
            i[index++] = 4; i[index++] = 6;
            i[index++] = 5; i[index++] = 7;

            i[index++] = 0; i[index++] = 4;
            i[index++] = 1; i[index++] = 5;
            i[index++] = 2; i[index++] = 6;
            i[index++] = 3; i[index++] = 7;

            vb.SetData(v);
            ib.SetData(i);
            device.SetVertexBuffer(vb);
            device.Indices = ib;
            device.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0, 8, 0, 12);
        }
        public static void DrawBox(GraphicsDevice device, Color color, BoundingBox box)
        {
            DrawBox(device, color, box.Min, box.Max);
        }
        public static void DrawBox(GraphicsDevice device, Color color, Vector3 start, Vector3 end)
        {
            VertexPositionColor[] v = new VertexPositionColor[8];
            int[] i = new int[24];
            VertexBuffer vb = new VertexBuffer(device, VertexPositionColor.VertexDeclaration, 8, BufferUsage.WriteOnly);
            IndexBuffer ib = new IndexBuffer(device, typeof(int), 24, BufferUsage.WriteOnly);

            for (int y = 0; y < 8; y++)
                v[y].Color = color;

            v[0].Position = start;
            v[1].Position = new Vector3(start.X, start.Y, end.Z);
            v[2].Position = new Vector3(end.X, start.Y, end.Z);
            v[3].Position = new Vector3(end.X, start.Y, start.Z);
            v[4].Position = new Vector3(start.X, end.Y, start.Z);
            v[5].Position = new Vector3(start.X, end.Y, end.Z);
            v[6].Position = end;
            v[7].Position = new Vector3(end.X, end.Y, start.Z);

            int index = 0;

            i[index++] = 0; i[index++] = 1;
            i[index++] = 1; i[index++] = 2;
            i[index++] = 2; i[index++] = 3;
            i[index++] = 3; i[index++] = 0;

            i[index++] = 4; i[index++] = 5;
            i[index++] = 5; i[index++] = 6;
            i[index++] = 6; i[index++] = 7;
            i[index++] = 7; i[index++] = 4;

            i[index++] = 0; i[index++] = 4;
            i[index++] = 1; i[index++] = 5;
            i[index++] = 2; i[index++] = 6;
            i[index++] = 3; i[index++] = 7;

            vb.SetData(v);
            ib.SetData(i);
            device.SetVertexBuffer(vb);
            device.Indices = ib;
            device.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0, 8, 0, 12);
        }
        public static void DrawSphere(GraphicsDevice device, Color color, Vector3 center, float radius, int tessellation, bool bFill)
        {
            int verticalSegments = tessellation;
            int horizontalSegments = tessellation * 2;

            VertexPositionColor[] v = new VertexPositionColor[horizontalSegments * (verticalSegments - 1) + 2];
            int[] ind = new int[6 * horizontalSegments * (verticalSegments - 1)];
            int index = 0;

            v[index].Color = color;
            //v[index].Normal = Vector3.Down;
            v[index++].Position = Vector3.Down * radius + center; 

            for (int i = 0; i < verticalSegments - 1; i++)
            {
                float latitude = ((i + 1) * MathHelper.Pi / verticalSegments) - MathHelper.PiOver2;

                float dy = (float)Math.Sin(latitude);
                float dxz = (float)Math.Cos(latitude);

                for (int j = 0; j < horizontalSegments; j++)
                {
                    float longitude = j * MathHelper.TwoPi / horizontalSegments;

                    float dx = (float)Math.Cos(longitude) * dxz;
                    float dz = (float)Math.Sin(longitude) * dxz;

                    Vector3 normal = new Vector3(dx, dy, dz);

                    v[index].Color = color;
                    //v[index].Normal = normal;
                    v[index++].Position = normal * radius + center;
                }
            }

            v[index].Color = color;
            //v[index].Normal = Vector3.Up;
            v[index++].Position = Vector3.Up * radius + center;

            index = 0;
            for (int i = 0; i < horizontalSegments; i++)
            {
                ind[index++] = 0;
                ind[index++] = 1 + (i + 1) % horizontalSegments;
                ind[index++] = 1 + i;
            }

            for (int i = 0; i < verticalSegments - 2; i++)
            {
                for (int j = 0; j < horizontalSegments; j++)
                {
                    int nextI = i + 1;
                    int nextJ = (j + 1) % horizontalSegments;

                    ind[index++] = 1 + i * horizontalSegments + j;
                    ind[index++] = 1 + i * horizontalSegments + nextJ;
                    ind[index++] = 1 + nextI * horizontalSegments + j;

                    ind[index++] = 1 + i * horizontalSegments + nextJ;
                    ind[index++] = 1 + nextI * horizontalSegments + nextJ;
                    ind[index++] = 1 + nextI * horizontalSegments + j;
                }
            }

            for (int i = 0; i < horizontalSegments; i++)
            {
                ind[index++] = v.Length - 1;
                ind[index++] = v.Length - 2 - (i + 1) % horizontalSegments;
                ind[index++] = v.Length - 2 - i;
            }

            VertexBuffer vb = new VertexBuffer(device, VertexPositionColor.VertexDeclaration, v.Length, BufferUsage.WriteOnly);
            IndexBuffer ib = new IndexBuffer(device, typeof(int), ind.Length, BufferUsage.WriteOnly);
            vb.SetData(v);
            ib.SetData(ind);
            device.SetVertexBuffer(vb);
            device.Indices = ib;

            RasterizerState r = device.RasterizerState;

            FillMode fm = bFill ? FillMode.Solid : FillMode.WireFrame;

            device.RasterizerState = new RasterizerState()
            {
                CullMode = CullMode.None,
                FillMode = fm
            }; 
            
            device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, v.Length, 0, ind.Length / 3);
            device.RasterizerState = r;
        }
        public static void DrawPlane(GraphicsDevice device, Vector3 color, Vector4 p, float scale) { DrawPlane(device, color, p, scale, Vector3.Zero); }
        public static void DrawPlane(GraphicsDevice device, Vector3 color, Vector4 p, float scale, Vector3 center)
        {
            VertexPositionColor[] v = new VertexPositionColor[4];
            VertexBuffer vb = new VertexBuffer(device, VertexPositionColor.VertexDeclaration, 4, BufferUsage.WriteOnly);
            IndexBuffer ib = new IndexBuffer(device, typeof(int), 4, BufferUsage.WriteOnly);

            Vector3 up = Vector3.Normalize(new Vector3(p.X, p.Y, p.Z));
            Matrix r = XMath.RotationXZFromUp(up);
            Vector3 fwd = Vector3.Transform(Vector3.Forward, r) * scale;
            Vector3 right = Vector3.Transform(Vector3.Right, r) * scale;
            Vector3 c = up * p.W + new Vector3(center.X, 0, center.Z);

            v[0].Position = c - fwd - right;
            v[1].Position = c + fwd - right;
            v[2].Position = c - fwd + right;
            v[3].Position = c + fwd + right;
            v[0].Color = v[1].Color = v[2].Color = v[3].Color = new Color(color);

            RasterizerState rs = device.RasterizerState;
            device.RasterizerState = new RasterizerState()
            {
                CullMode = CullMode.None
            };

            vb.SetData(v);
            ib.SetData(new int[]{0,1,2,3});
            device.SetVertexBuffer(vb);
            device.Indices = ib;
            device.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, 0, 0, 4, 0, 2);

            device.RasterizerState = rs;
        }
        public static void DrawLine(GraphicsDevice device, Vector3 color, Vector3 s, Vector3 e)
        {
            DrawLine(device, new Color(color), s, e);
        }
        public static void DrawLine(GraphicsDevice device, Color color, Vector3 s, Vector3 e)
        {
            VertexPositionColor[] v = new VertexPositionColor[2];
            VertexBuffer vb = new VertexBuffer(device, VertexPositionColor.VertexDeclaration, 2, BufferUsage.WriteOnly);
            IndexBuffer ib = new IndexBuffer(device, typeof(int), 2, BufferUsage.WriteOnly);

            v[0].Position = s;
            v[1].Position = e;
            v[0].Color = v[1].Color = color;

            RasterizerState rs = device.RasterizerState;
            device.RasterizerState = new RasterizerState()
            {
                CullMode = CullMode.None
            };

            vb.SetData(v);
            ib.SetData(new int[] { 0, 1 });
            device.SetVertexBuffer(vb);
            device.Indices = ib;
            device.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0, 2, 0, 1);

            device.RasterizerState = rs;
        }
    }
}
